package pool

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"reflect"
	"runtime"

	"github.com/ivpusic/grpool"
)

var Job = grpool.NewPool(runtime.NumCPU()*200, runtime.NumCPU()*20)

var (
	DefRec = NewRecover(os.Stderr)
)

type RecoverFuncType func() (e error)
type Recover struct {
	out io.Writer
}

func NewRecover(out io.Writer) *Recover {
	return &Recover{out: out}
}

func (r *Recover) SetOut(out io.Writer) {
	r.out = out
}

func (r *Recover) GetOut() io.Writer {
	return r.out
}

func (r *Recover) Run(funcType RecoverFuncType) {
	r.ReturnFunc(funcType)()
}

func (r *Recover) ReturnFunc(funcType RecoverFuncType) func() {
	return RecoverFunc(funcType, r.out)
}

func RecoverFunc(fun RecoverFuncType, w io.Writer) func() {
	logger := log.New(w, "[Jobs]", log.Ldate|log.Ltime|log.Lmicroseconds|log.Llongfile)
	return func() {
		var err error
		defer func() {
			if p := recover(); p != nil {
				err = fmt.Errorf("%v", p)
				logger.Printf("\n[Panic]:%v;\n%s\n\n", p, PanicTrace(4))
			}
			if err != nil {
				pc := reflect.ValueOf(fun).Pointer()
				fpc := runtime.FuncForPC(pc)
				funcName := fpc.Name()
				fl, line := fpc.FileLine(pc)
				logger.Printf("\n[Error][Func]:%s;\n[File]:%s;[Line]:%d;\n[Text]:%s;\n\n",
					funcName, fl, line, err.Error())
			}
		}()
		err = fun()
	}
}

// PanicTrace trace panic stack info.
func PanicTrace(kb int) []byte {
	s := []byte("/src/runtime/panic.go")
	e := []byte("\ngoroutine ")
	line := []byte("\n")
	stack := make([]byte, kb<<10) //4KB
	length := runtime.Stack(stack, false)
	start := bytes.Index(stack, s)
	stack = stack[start:length]
	start = bytes.Index(stack, line) + 1
	stack = stack[start:]
	end := bytes.LastIndex(stack, line)
	if end != -1 {
		stack = stack[:end]
	}
	end = bytes.Index(stack, e)
	if end != -1 {
		stack = stack[:end]
	}
	stack = bytes.TrimRight(stack, "\n")
	return stack
}
